﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

using HtmlAgilityPack;
using HRT;
using ICSharpCode.SharpZipLib.Zip;
using LucasCode.PublicTypes;
using Encoder=System.Drawing.Imaging.Encoder;
using System.Globalization;
using System.Collections.Specialized;
using System.Diagnostics;

namespace IoWorker
{
    public class ParseException : Exception
    {
        public string HTMLSource = string.Empty;

        public ParseException(string error, string html)
            : base(error)
        {
            HTMLSource = html;
        }
    };
    public static class WebIO
    {
        /*public class UploadSetting
        {
            public int JpegCompression = 80;
        };
        private static readonly UploadSetting UploadSettings = new UploadSetting();*/

        private const UInt32 m_WebUploadBufferSize = 2048;
        
        private static int m_SpeedMeasurementInterval = 500;
        //private static string m_Status = string.Empty;
        

        public static int SpeedMeasurementInterval
        {
            get { return m_SpeedMeasurementInterval; }
            set { m_SpeedMeasurementInterval = value; }
        }

        public static string getIDFromLink(string link)
        {
            return Regex.Match(link, @"/(\d+)/*$", RegexOptions.Compiled).Groups[1].Value;
        }

        public static string GetHTML(string URL)
        {
            return GetHTML(URL, String.Empty);
        }

        public static string GetHTML(string URL, string cook)
        {
            return GetHTML(URL, new Cookie("COOKIE", cook, "/", ".esl.eu"));
        }

        public static string GetHTML(string URL, Cookie cook)
        {
            string HTML = String.Empty;
            
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
            request.Timeout = 30000;
            if (cook != null)
            {
                request.CookieContainer = new CookieContainer();
                request.CookieContainer.Add(cook);
            }
            using (WebResponse response = request.GetResponse())
            {
                using (StreamReader r = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    HTML = r.ReadToEnd();
                }
            }
            return HTML;
        }

        public static Bitmap GetBitmapFromURL(string URL)
        {
            Bitmap b = null;


            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
            request.Timeout = 30000;
            using (WebResponse response = request.GetResponse())
            {
                using (Stream s = response.GetResponseStream())
                {
                    b = new Bitmap(s);
                }
            }
            return b;
        }

        public static MatchArchive GetArchiveForURL(string url)
        {
            return GetArchiveForURL(url, string.Empty);
        }
        public static MatchArchive GetArchiveForURL(string url, string cookie)
        {
            MatchArchive a = new MatchArchive();
            a.Matchlink = url;
            return UpdateDetailsFromWeb(cookie, a);
        }
        public static MatchArchive UpdateDetailsFromWeb(string cookie, MatchArchive matchArchive)
        {
            System.Diagnostics.Trace.WriteLine("WebSynchronizer UpdateDetailsFromWeb for match " + matchArchive.Matchlink);
            DateTime start = DateTime.Now;

            if (string.IsNullOrEmpty(matchArchive.Matchlink))
                return null;
            //UpdateProgress(10);
            string HTML = WebIO.GetHTML(matchArchive.Matchlink, cookie);
            System.Diagnostics.Trace.WriteLine("Web Request took " + DateTime.Now.Subtract(start));
            start = DateTime.Now;
            //UpdateProgress(50);

            var d = new HtmlDocument();
            d.LoadHtml(HTML);

            System.Diagnostics.Trace.WriteLine("START UpdateDetailsFromWeb HTML DUMP--------------------------------------------");
            System.Diagnostics.Trace.WriteLine("///////////////////////////////////////////////////////////");
            System.Diagnostics.Trace.WriteLine(HTML);
            System.Diagnostics.Trace.WriteLine("///////////////////////////////////////////////////////////");
            System.Diagnostics.Trace.WriteLine("END UpdateDetailsFromWeb HTML DUMP----------------------------------------------");


            HtmlNode matchinfos = d.DocumentNode.SelectSingleNode(@"//div[@id='main_content']/table");


            //alle parameter des matches:
            // //div[@id='main_content']//td[@colspan='2' and text()='Parameters']/../following-sibling::tr[1]/td/table/tr/td[text()='MatchID']/../../tr


            HtmlNodeCollection Points = matchinfos.SelectNodes("tr[5]/td/table/tr[position()<3]/td/b");
            HtmlNodeCollection Maps = matchinfos.SelectNodes("//td[@colspan='2' and text()='Parameters']/../following-sibling::tr[1]/td/table/tr/td[text()='map']/following-sibling::td | //td[@class='WindowHeader' and text()='Result']/../following-sibling::tr[1]/td/table[@cellpadding='2']/tr[position()<=2]/td[1]");

            HtmlNodeCollection Contestants = matchinfos.SelectNodes("tr[3]/td/table/tr[2]/td/a[1]");

            HtmlNodeCollection Aequitas = matchinfos.SelectNodes(@"//tr[@id='aquetias_public_logs']/td/table/tr");

            HtmlNodeCollection MatchDateInfo = matchinfos.SelectNodes(@"tr[3]/td[2]/table/tr[3]/td[2]");

            HtmlNodeCollection Lineup;

            if (matchinfos.SelectNodes("tr[6]/td")[0].InnerText == "Matchmedia Requests")
            {
                if (matchinfos.SelectNodes("tr[8]/td")[0].InnerText == "Lineup")
                    Lineup = matchinfos.SelectNodes("tr[9]/td/table/tr[2]");
            }
            else if (matchinfos.SelectNodes("tr[6]/td")[0].InnerText == "Lineup")
                Lineup = matchinfos.SelectNodes("tr[7]/td/table/tr[2]");


            HtmlNode ladder = d.DocumentNode.SelectSingleNode(@"//div[@id='navi_breadcrumb']/div/b/a");
            if (ladder != null)
                matchArchive.Ladder = ladder.InnerText.Trim();

            matchArchive.MatchDate = ServerTimeStringToDateTime(MatchDateInfo[0].InnerText);



            for (int i = 0; i < Points.Count / 2; i++)
            {
                var r = new MatchRound();
                if (Maps != null)
                {
                    r.Map = Maps[i].InnerText;
                }

                try
                {
                    r.Scores[0] = int.Parse(Points[i * 2].InnerText);
                    r.Scores[1] = int.Parse(Points[(i * 2) + 1].InnerText);
                }
                catch
                {
                    System.Diagnostics.Trace.WriteLine("No result entered yet, can't fill score");
                }

                matchArchive.AddRound(r);
            }




            /* this is what a teamid list in the ESL source looks like
            ESLteamID.unshift(2470639);
            ESLteamID.unshift(1645201);
            ESLteamID.unshift(2804185);
            ESLteamID.unshift(2658316);
            ESLteamID.unshift(2466101);
            ESLteamID.unshift(2466119);
            ESLteamID.unshift(2659890);
            ESLteamID.unshift(2489288);
            ESLteamID.unshift(1642763); */




            
                matchArchive.OwnTeam = null;
                MatchCollection MyTeamIDs = Regex.Matches(HTML, @"\bESLteamID.unshift\((\d+)\);");
                var TeamIDs = new StringCollection();

                MatchContestant team;

                foreach (Match m in MyTeamIDs)
                {
                    TeamIDs.Add(m.Groups[1].Value);
                }
                foreach (HtmlNode contestant in Contestants)
                {
                    if (matchArchive.containsContestant(WebIO.getIDFromLink(contestant.Attributes["href"].Value)))
                    {
                        team = matchArchive.getContestant(WebIO.getIDFromLink(contestant.Attributes["href"].Value));
                    }
                    else
                    {
                        team = new MatchContestant();

                        team.ID = WebIO.getIDFromLink(contestant.Attributes["href"].Value);
                        //TODO: re-enable or remove the logo-fetching part
                        //team.Logo = WebIO.GetBitmapFromURL(contestant.SelectSingleNode("img").Attributes["src"].Value);
                        team.Name = contestant.InnerText.Trim();

                        matchArchive.AddContestant(team);
                    }

                    if (contestant.Attributes["href"].Value.Contains("/player/"))
                    {
                        team.Type = MatchContestantTypes.Individual;
                        var p = new MatchPlayer();

                        p.CurrentNick = team.Name;
                        p.ESLNick = team.Name;
                        p.UserID = team.ID;

                        team.addPlayer(p);
                    }

                    if (TeamIDs.Contains(team.ID))
                        matchArchive.OwnTeam = team;
                }


                matchArchive.Owner = null;
                string MyUserID = string.Empty;
                Match MyUserIDRegex = Regex.Match(HTML, @"ESLuserID = (\d+)");

                MatchPlayer player;

                if (MyUserIDRegex.Success)
                    MyUserID = MyUserIDRegex.Groups[1].Value;
                if (Aequitas != null)
                    foreach (HtmlNode n in Aequitas)
                    {
                        if (matchArchive.containsPlayer(WebIO.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value)))
                        {
                            player =
                                matchArchive.getPlayer(
                                    WebIO.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value));

                            if (string.IsNullOrEmpty(player.ESLNick))
                                player.ESLNick = n.SelectSingleNode("td[1]/a").InnerText;

                            if (string.IsNullOrEmpty(player.CurrentNick))
                                player.CurrentNick = player.ESLNick;

                            player.HasAequitasLog = true;
                        }
                        else
                        {
                            player = new MatchPlayer();

                            player.UserID = WebIO.getIDFromLink(n.SelectSingleNode("td[1]/a").Attributes["href"].Value);
                            player.ESLNick = n.SelectSingleNode("td[1]/a").InnerText;
                            player.CurrentNick = player.ESLNick;
                            player.HasAequitasLog = true;
                            //TODO: re-enable or remove this
                            //player.GameAccounts = WebIO.getGameAccountsForID(player.UserID);

                            if (player.UserID == MyUserID)
                                matchArchive.Owner = player;
                        }

                        matchArchive.AddUserwithAeq(player.UserID);

                        string teamid = WebIO.getIDFromLink(n.SelectSingleNode("td[2]/a").Attributes["href"].Value);

                        bool foundteam = false;


                        foreach (MatchContestant contestant in matchArchive.Contestants)
                        {
                            if (foundteam)
                                break;
                            switch (contestant.Type)
                            {
                                case MatchContestantTypes.Individual:
                                    if (contestant.ID == player.UserID)
                                    {
                                        foundteam = true;
                                        contestant.Players[0].HasAequitasLog = true;
                                        //c.addPlayer(player);
                                        break;
                                    }
                                    break;
                                case MatchContestantTypes.Team:
                                    if (contestant.ID == teamid)
                                    {
                                        foundteam = true;
                                        if (!contestant.containsPlayer(player))
                                            contestant.addPlayer(player);
                                        break;
                                    }
                                    break;
                            }
                        }

                        if (!foundteam)
                        {
                            bool dupfound = false;
                            foreach (MatchContestant contestant in matchArchive.Contestants)
                            {
                                if (contestant.searchDuplicate(player) != null)
                                {
                                    contestant.addPlayer(player);
                                    dupfound = true;
                                    break;
                                }
                            }
                            if (!dupfound)
                            {
                                foreach (MatchPlayer p in matchArchive.TeamlessPlayers)
                                    if (p.CompareTo(player) == 0)
                                        dupfound = true;
                                if (!dupfound)
                                    matchArchive.AddTeamlessPlayer(player);

                            }

                        }
                    }

            System.Diagnostics.Trace.WriteLine("WebSynchronizer UpdateDetailsFromWeb successfully proceeded for match: " + matchArchive.Matchlink + ". Took " + DateTime.Now.Subtract(start));

            return matchArchive;
        }
        public static DateTime ServerTimeStringToDateTime(string dateTimeString)
        {
            Match serverTimeMatch = Regex.Match(dateTimeString, @"((\d\d)[\./](\d\d)[\./](\d\d) (\d\d:\d\d))");
            if (!serverTimeMatch.Success)
                throw new ParseException("Invalid server time string", dateTimeString);
            string ServerTimeString = serverTimeMatch.Groups[1].Value.Replace('/', '.');

            return DateTime.Parse(ServerTimeString, CultureInfo.GetCultureInfo("de-DE"));
        }
        public static MatchArchive[] getOpenMatches(string cookie)
        {
            System.Diagnostics.Trace.WriteLine("WebIO getOpenMatches(string cookie)");

            var matchlist = new List<MatchArchive>();
            //get html source
            DateTime myTime = DateTime.Now;

            string html = WebIO.GetHTML("http://www.esl.eu/change_lang/en/?ref=/open_matches/", cookie);
            var d = new HtmlDocument();
            d.LoadHtml(html);

            System.Diagnostics.Trace.WriteLine("START Open Matches HTML DUMP--------------------------------------------");
            System.Diagnostics.Trace.WriteLine("///////////////////////////////////////////////////////////");
            System.Diagnostics.Trace.WriteLine(html);
            System.Diagnostics.Trace.WriteLine("///////////////////////////////////////////////////////////");
            System.Diagnostics.Trace.WriteLine("END Open Matches HTML DUMP----------------------------------------------");

            DateTime ServerTime = DateTime.MinValue;


            HtmlNode navi = d.DocumentNode.SelectSingleNode(@"//div[@id='intronav']");
            if (navi == null)
                throw new ParseException("Could not parse website header!", html);

            bool isLoggedIn = navi.InnerText.Contains("Logout");
            if (!isLoggedIn)
                throw new ParseException("Invalid login!", html);

            HtmlNode ServerTimeNode = d.DocumentNode.SelectSingleNode(@"//div[@id='navi_options']/div[1]");
            //TODO: new layout, remove old one if design reaches final
            if (ServerTimeNode == null)
                ServerTimeNode = d.DocumentNode.SelectSingleNode(@"//span[@id='userbar_timezone_time']");

            if (ServerTimeNode == null)
                throw new ParseException("Could not parse servertime", html);
            else
            {
                ServerTime = ServerTimeStringToDateTime(ServerTimeNode.InnerText);

                System.Diagnostics.Trace.WriteLine("ESL Server Time: " + ServerTime);
            }

            TimeSpan TimeDiff = ServerTime - myTime;

            System.Diagnostics.Trace.WriteLine("Starting to parse //div[@id='main_content']");

            HtmlNode matchinfos = d.DocumentNode.SelectSingleNode(@"//div[@id='main_content']");
            if (matchinfos == null)
            {
                System.Diagnostics.Trace.WriteLine("Could not find //div[@id=\"main_content\"]");
                throw new ParseException("Could not get main content!", html);
            }
            // first assume the user is a premium user and select the node WITH a form tag in the path
            HtmlNodeCollection matches = matchinfos.SelectNodes("div/form/table/tr[position()>=3]");

            // the user may not be a premium member, so try this:
            if (matches == null)
                matches = matchinfos.SelectNodes("div/table/tr[position()>=3]");
            else
                System.Diagnostics.Trace.WriteLine("Premium User recognized while looking for matches");

            //do we have some matches?
            if (matches != null)
            {
                foreach (HtmlNode n in matches)
                {
                    HtmlNode check = n.SelectSingleNode("td[3]");

                    if (check == null)
                    {
                        System.Diagnostics.Trace.WriteLine("Accidently thought there is an open match, while there was none");
                        continue;
                    }

                    // checking if this match is an unanswered challenge, if so, continue with next match
                    if (check.InnerText.Trim().Substring(check.InnerText.Trim().Length - 4) != "open")
                    {
                        System.Diagnostics.Trace.WriteLine("Open challenge vs " + n.SelectSingleNode("td[2]").InnerText.Trim() + ", doesn't count as open match");
                        continue;
                    }

                    Match datestrMatch = Regex.Match(n.SelectSingleNode("td[1]").InnerText.Trim(), @"((\d\d)[\./](\d\d)[\./](\d\d) (\d\d:\d\d))");

                    string datestr = datestrMatch.Groups[1].Value.Replace('/', '.');

                    DateTime matchdate = DateTime.Now;

                    if (!string.IsNullOrEmpty(datestr))
                    {
                        matchdate = DateTime.Parse(datestr, CultureInfo.GetCultureInfo("de-DE")).Add(TimeDiff);
                    }
                    else
                    {
                        System.Diagnostics.Trace.WriteLine("Failed parsing matchtime after open match was recognized");
                        throw new ParseException("Could not parse matchtime", html);
                    }

                    string enemy = n.SelectSingleNode("td[2]").InnerText.Trim();
                    string team = Regex.Match(n.ParentNode.SelectSingleNode("tr[1]//span").InnerText.Trim(), @"^\( (.*?) \)$").Groups[1].Value;
                    string enemyurl = n.SelectSingleNode("td[2]//a[@href]").Attributes["href"].Value.Trim();
                    string enemyid = WebIO.getIDFromLink(enemyurl);

                    string matchurl = n.SelectSingleNode("td[5]//a[@href]").Attributes["href"].Value.Trim();

                    System.Diagnostics.Trace.WriteLine(String.Format("Match vs. {0} @ {1} (Offset {2})", enemy, matchdate, TimeDiff));

                    MatchArchive a = new MatchArchive();
                    //teams
                    var teams = new MatchContestant[] { new MatchContestant(), new MatchContestant() };
                    teams[0].Name = team;
                    teams[1].Name = enemy;
                    teams[1].ID = enemyid;
                    a.Contestants = teams;
                    a.OwnTeam = teams[0];
                    //matchinfo
                    a.MatchDate = matchdate;

                    //a.League = league;
                    a.Matchlink = matchurl;
                    matchlist.Add(a);
                }
            }

            System.Diagnostics.Trace.WriteLine("WebIO getOpenMatches successfully finished");

            return matchlist.ToArray();
        }

        public static void updatePlayerInformationFromWebsite(MatchPlayer p)
        {
            System.Diagnostics.Trace.WriteLine("WebIO updatePlayerInformationFromWebsite for gameID " + p.GameID + " at " + DateTime.Now);

            if (string.IsNullOrEmpty(p.GameID))
                return;
            var d = new HtmlDocument();
            d.LoadHtml(GetHTML(@"http://www.esl.eu/search/?query=" + p.GameID));

            HtmlNode result =
                d.DocumentNode.SelectSingleNode(
                    @"//div[@id=""main_content""]/div/div/div[@class=""searchresult""]/a[@href]");
            
            if (result != null)
            {
                p.ESLNick = result.InnerText;
                p.UserID = getIDFromLink(result.Attributes["href"].Value);
            }

            System.Diagnostics.Trace.WriteLine("WebIO updatePlayerInformationFromWebsite finished at " + DateTime.Now);
        }

        public static MatchPlayer lookupPlayerByGameID(string gameID)
        {
            System.Diagnostics.Trace.WriteLine("WebIO lookupPlayerByGameID for gameID " + gameID + " at " + DateTime.Now);

            if (string.IsNullOrEmpty(gameID))
                return null;
            var d = new HtmlDocument();
            d.LoadHtml(GetHTML(@"http://www.esl.eu/search/?query=" + gameID));

            HtmlNode result =
                d.DocumentNode.SelectSingleNode(
                    @"//div[@id=""main_content""]/div/div/div[@class=""searchresult""]/a[@href]");
            
            if (result != null)
            {
                var p = new MatchPlayer(result.InnerText);
                p.ESLNick = result.InnerText;
                p.GameID = gameID;
                p.UserID = getIDFromLink(result.Attributes["href"].Value);
                
                return p;
            }

            System.Diagnostics.Trace.WriteLine("WebIO lookupPlayerByGameID finished at " + DateTime.Now);
            
            return null;
        }

        public static GameAccount[] getGameAccountsForID(string userID)
        {
            System.Diagnostics.Trace.WriteLine("WebIO getGameAccountsForID " + userID + " at " + DateTime.Now);

            if (string.IsNullOrEmpty(userID))
                return null;

            var d = new HtmlDocument();

            d.LoadHtml(GetHTML(@"http://www.esl.eu/player/gameaccounts/" + userID));

            HtmlNodeCollection result =
                d.DocumentNode.SelectNodes(@"//div[@id=""main_content""]/div[3]/table/tr[position()>1]");

            List<GameAccount> gameAccounts = new List<GameAccount>();

            GameAccount temp;

            foreach (HtmlNode gameaccount in result)
            {
                temp = new GameAccount(gameaccount.SelectSingleNode(@"td[1]").InnerText,
                                       gameaccount.SelectSingleNode(@"td[2]").InnerText.Trim().Split('\n')[0],
                                       DateTime.Parse(gameaccount.SelectSingleNode(@"td[3]").InnerText));

                gameAccounts.Add(temp);
            }

            System.Diagnostics.Trace.WriteLine("WebIO getGameAccountsForID " + userID + " successfully finished at " + DateTime.Now);

            return gameAccounts.ToArray();
        }

        public static MatchPlayer[] getMemberList(string teamID)
        {
            System.Diagnostics.Trace.WriteLine("WebIO getMemberlist for team " + teamID + " started.");

            var players = new List<MatchPlayer>();
            if (string.IsNullOrEmpty(teamID))
                return players.ToArray();

            var d = new HtmlDocument();
            string html = GetHTML(@"http://www.esl.eu/team/members/" + teamID);
            d.LoadHtml(html);

            HtmlNode MainNode = d.DocumentNode.SelectSingleNode(@"//div[@id=""main_content""]");

            HtmlNodeCollection members = MainNode.SelectNodes(@"div/table/tr/td/table/tr/td/b/a");
            if (members == null)
                return players.ToArray();

            var barredmemberids = new System.Collections.Specialized.StringCollection();
            
            HtmlNodeCollection BarredMembersNodes = MainNode.SelectNodes(@"div/div/table/tr");
            if (BarredMembersNodes != null)
            {
                foreach (HtmlNode n in BarredMembersNodes)
                {
                    barredmemberids.Add(getIDFromLink(n.SelectSingleNode("td[2]/a").Attributes["href"].Value));
                }
            }
            
            foreach (HtmlNode n in members)
            {
                MatchPlayer p = new MatchPlayer(n.InnerText);
                p.ESLNick = n.InnerText;
                p.CurrentNick = n.InnerText;
                p.UserID = getIDFromLink(n.Attributes["href"].Value);
                
                if (barredmemberids.Contains(p.UserID))
                    p.IsBarred = true;

                HtmlNode GameIDNode = n.SelectSingleNode(@"../../../../../../td[5]");
                if (GameIDNode != null)
                {
                    foreach (HtmlNode GameID in GameIDNode.SelectNodes("div"))
                    {
                        string[] gameaccount = GameID.InnerText.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries);
                        //valid gameaccount with at least type and value?
                        if (gameaccount.Length < 2)
                            continue;
                        string IDType = gameaccount[0].Trim();
                        //strip ":" from ID-type
                        if (IDType.EndsWith(":"))
                            IDType = IDType.Substring(0, IDType.Length - 1);
                        string id = gameaccount[1].Trim();
                        p.GameAccountList[IDType].Value = id;
                    }
                }
                players.Add(p);
            }

            System.Diagnostics.Trace.WriteLine("WebIO getMemberlist for team " + teamID + " successfully finished.");

            return players.ToArray();
        }

        public static void UploadMatchFile(string cookie, UploadFile file, string matchlink)
        {
            using (Stream data = Compression.GetUploadStream(file))
            {
                file.ResetTransferStats(data.Length);
                string HTML = string.Empty;

                StreamReader html;

                int bytes;
                var encoding = new UTF8Encoding();

                //String boundary = "AaB03x";
                string matchid = getIDFromLink(matchlink);
                // application/zip oder image/jpeg
                string type, name;
                if (file.MatchFile.FileType == MatchFileType.Screenshot)
                {
                    type = "image/jpeg";
                    name = Path.GetFileNameWithoutExtension(file.MatchFile.Filename) + ".jpg";
                }
                else
                {
                    type = "application/zip";
                    name = Path.GetFileNameWithoutExtension(file.MatchFile.Filename) + ".zip";
                }

                string content = String.Format(IoHandler.IOStrings.RequestString, matchid, file.MatchFile.TargetName, name, type);
                var buffer = new byte[m_WebUploadBufferSize];
                file.Status = IoHandler.IOStrings.Uploading;
                
                try
                {
                    Uri matchUri = new Uri(file.MatchFile.Parent.Matchlink);
                    string query = matchUri.PathAndQuery;
                    query = Regex.Replace(query, @"(.*?)match/\d+(.*?)", @"$1matchmedia/savedata$2");
                    Uri uploadUri = new Uri(new Uri("http://upload.esl.eu"), query);

                    HttpWebRequest r = (HttpWebRequest)WebRequest.Create(uploadUri);
                    r.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
                    r.Timeout = Timeout.Infinite;
                    r.KeepAlive = false;
                    r.ProtocolVersion = HttpVersion.Version10;
                    r.CookieContainer = new CookieContainer();
                    r.CookieContainer.Add(new Cookie("COOKIE", cookie, "/", ".esl.eu"));
                    r.Method = "POST";
                    r.ContentType = "multipart/form-data; boundary=AaB03x";
                    r.ContentLength = data.Length + encoding.GetByteCount(content) + encoding.GetByteCount(IoHandler.IOStrings.RequestTermination);

                    using (Stream output = r.GetRequestStream())
                    {
                        output.Write(encoding.GetBytes(content), 0, encoding.GetByteCount(content));
                        while (!file.Aborted && (bytes = data.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            output.Write(buffer, 0, bytes);
                            file.TransferredBytesDelta(bytes);
                        }
                        file.ResetTransferStats();
                        file.Progress = 100;
                        output.Write(encoding.GetBytes(IoHandler.IOStrings.RequestTermination), 0, encoding.GetByteCount(IoHandler.IOStrings.RequestTermination));
                    }

                    if (file.Aborted)
                        throw new UploadException(IoHandler.IOStrings.UploadAborted);

                    using (HttpWebResponse response = (HttpWebResponse)r.GetResponse())
                    {
                        using (html = new StreamReader(response.GetResponseStream()))
                            HTML = html.ReadToEnd();
                        if (!string.IsNullOrEmpty(HTML))
                        {
                            //TODO: is this correct?
                            if (Regex.Match(HTML, @"<title>Electronic Sports League is currently offline</title>").Success)
                                throw new UploadException(IoHandler.IOStrings.ServerError);
                            Match userID = Regex.Match(HTML, @"ESLuserID\s*=\s*(\d+);", RegexOptions.Singleline);
                            // the userID should be always present when parsing a valid response (even though the id might be 0)
                            if (!userID.Success)
                            {
                                // this case should not exist anymore since 2010, but keeping it doesn't hurt
                                Trace.WriteLine("User info not found in upload response.");
                                Trace.WriteLine(HTML);
                                Trace.WriteLine("Uploader bailing out.");
                                throw new UploadException(IoHandler.IOStrings.ServerError);
                            }
                            else if (userID.Groups[1].Value == "0")
                            {
                                // this still applies for the 2010 redesign
                                Trace.WriteLine("User cookie wrong/outdated");
                                throw new UploadException(IoHandler.IOStrings.WrongCredentials);
                            }
                        }
                        else if (response.StatusCode == HttpStatusCode.OK)
                        {
                            // this is ok
                            // no response means success in the the 2010 upload system
                        }
                        else
                        {
                            Trace.WriteLine("Upload response is empty and has return code " + response.StatusCode);
                            throw new UploadException(IoHandler.IOStrings.ServerError);
                        }
                    }
                }
                catch (WebException e)
                {
                    if (e.Status == WebExceptionStatus.RequestCanceled)
                        throw new UploadException(IoHandler.IOStrings.UploadAborted);
                }
                file.Status = IoHandler.IOStrings.Completed;
                file.MatchFile.WasUploaded = true;
            }
        }

        
    }
}
